/*
libfmail: Socket Based IPC mechanism

Copyright (C) 2007  Carlos Daniel Ruvalcaba Valenzuela <clsdaniel@gmail.com>

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

#include <stdio.h>
#include <string.h>
#include <malloc.h>

#include <odkutils.h>

#include <queue>

#include <libfmail/socket.h>
#include <libfmail/ipcmsg.h>
#include <libfmail/ipc.h>
#include <libfmail/socketipc.h>

SocketIPC::SocketIPC(Socket *s){
    sock = s;
	re = odk_regex_new("(\\w+)\\[(\\d+)\\]\\[(\\d+)\\]", 0, 0);
}

SocketIPC::SocketIPC(char *uri){
	char *str; 
        
    re = odk_regex_new("([\\w\\d\\.]+)\\:(\\d*)[/]*(\\w*)", 0, 0);
    m = odk_regex_match(re, uri, 0);
        
    str = odk_submatch_copy(uri, m, 0);
    if (str){
    	printf("Host: %s\n", str);
        host = str;
    }
        
    str = odk_submatch_copy(uri, m, 1);
    if (str){
    	port = atoi(str);
        printf("Port: %i\n", port);
        free(str);
    }
        
    str = odk_submatch_copy(uri, m, 2);
    if (str){
        printf("Options: %s\n", str);
        free(str);
    }
        
    printf("Started socket on: %s\n", uri);
    sock = new Socket();
    auxsock = NULL;
    odk_regex_free(re);
    odk_match_free(m);
        
    re = odk_regex_new("(\\w+)\\[(\\d+)\\]\\[(\\d+)\\]", 0, 0);
}

int SocketIPC::RequestIPC(){
    return sock->Connect(host, port);
}

int SocketIPC::ListenIPC(){
    sock->Bind(port);
    sock->Listen(10);
        
    auxsock = sock->Accept();
    return 0;
}
    
int SocketIPC::CloseIPC(){
    sock->Close();
    if (auxsock)
        auxsock->Close();
}
    
int SocketIPC::FetchMessage2(){
        char buffer[255], xbuff[255], tbuff[20], *msgdata;
        int state, onParse;
        int i, t, r, mlen, margc;
        IPCMessage *msg;
        
        state = 0;
        onParse = 1;
        
        mlen = 0;
        margc = 0;
        msg = NULL;
        
        if (auxsock == NULL)
            auxsock = sock;
            
        while (onParse){
        
            memset(buffer, 0, 255);
            r = auxsock->Read(buffer, 255);
            
            if (r < 1){
                return 0;
                state = -1;
                onParse = 0;
            }
            
            for (i = 0; i < r; i++){
                switch (state){
                case 0:
                    if (buffer[i] == 'M')
                        state++;
                    break;
                case 1:
                    if (buffer[i] == 'S')
                        state++;
                    break;
                case 2:
                    if (buffer[i] == 'G')
                        state++;
                    break;
                case 3:
                    if (buffer[i] == '['){
                        state++;
                        memset(tbuff, 0, 20);
                        t = 0;
                    }
                    break;
                case 4:
                    if ((buffer[i] >= 48) && (buffer[i] <= 57)){
                        tbuff[t] = buffer[i];
                        t++;
                        if (t == 20)
                            state = -1;
                    }else{
                        mlen = atoi(tbuff);
                        state++;
                    }
                    break;
                case 5:
                    if (buffer[i] == '['){
                        state++;
                        memset(tbuff, 0, 20);
                        t = 0;
                    }
                    break;
                case 6:
                    if ((buffer[i] >= 48) && (buffer[i] <= 57)){
                        tbuff[t] = buffer[i];
                        t++;
                        if (t == 20)
                            state = -1;
                    }else{
                        margc = atoi(tbuff);
                        state++;
                        if (mlen < 255){
                            msgdata = xbuff;
                        }else{
                            msgdata = (char*)malloc(sizeof(char) * mlen + 1);
                        }
                        t = 0;
                    }
                    break;
                case 7:
                    if (t < mlen){
                        msgdata[t] = buffer[i];
                        t++;
                        if (t == mlen){
                            msgdata[t] = 0;
                            state++;
                        }
                    }
                    break;
                case 8:
                    msg = new IPCMessage(msgdata);
                    if (msgdata != xbuff)
                        free(msgdata);
                    state++;
                
                    if (margc == 0)
                        state = 20;
                    i--;
                    break;
                case 9:
                    if (buffer[i] == 'P')
                        state++;
                    break;
                case 10:
                    if (buffer[i] == 'A')
                        state++;
                    break;
                case 11:
                    if (buffer[i] == 'R')
                        state++;
                    break;
                case 12:
                    if (buffer[i] == 'A')
                        state++;
                    break;
                case 13:
                    if (buffer[i] == 'M')
                        state++;
                    break;
                case 14:
                    if (buffer[i] == '['){
                        state++;
                        memset(tbuff, 0, 20);
                        t = 0;
                    }
                    break;
                case 15:
                    if ((buffer[i] >= 48) && (buffer[i] <= 57)){
                        tbuff[t] = buffer[i];
                        t++;
                        if (t == 20)
                            state = -1;
                    }else{
                        mlen = atoi(tbuff);
                        state++;
                    }
                    break;
                case 16:
                    if (mlen < 255){
                        msgdata = xbuff;
                    }else{
                        msgdata = (char*)malloc(sizeof(char) * mlen + 1);
                    }
                    t = 0;
                    state++;
                    i--;
                    break;
                case 17:
                    if (t < mlen){
                        msgdata[t] = buffer[i];
                        t++;
                        if (t == mlen){
                            msgdata[t] = 0;
                            state++;
                            i--;
                        }
                    }
                    break;
                case 18:
                    msg->PushParam(msgdata);
                    if (msgdata != xbuff)
                        free(msgdata);
                    if (msg->ParamCount() == margc){
                        state++;
                    }else{
                        state = 9;
                    }
                    onParse = 0;
                    break;
                default:
                    onParse = 0;
                    break;
                }   
            }
        }
        
        if (state == -1){
            if (msg)
                delete msg;
            return 0;
        }
        
        msgQueue.push(msg);
        return 1;
}

/*
int FetchMessage(){
        char buffer[255], *param;
        int i, j, k, s, r;
        IPCMessage *msg;
        
        memset(buffer, 0, 255);
        r = auxsock->Read(buffer, 255);
        
        if (r < 3)
            return 0;
        
        printf("buffer: %s\n", buffer);
        m = odk_regex_match(re, buffer, 0);
        
        if (m == NULL)
            return 0;
        printf("Regex Matched!!\n");
        if (buffer[0] != 'M')
            return 0;
        
        param = odk_submatch_copy(buffer, m, 1);
        i = atoi(param);
        free(param);
            
        param = odk_submatch_copy(buffer, m, 2);
        j = atoi(param);
        free(param);
        
        memset(buffer, 0, 255);
        r = auxsock->Read(buffer, 255);
          
        if (r != i)
            return 0;
            
        msg = new IPCMessage(buffer);
        for (i = 0; i < j; i++){
            auxsock->Read(buffer, 255);
            m = odk_regex_match(re, buffer, 0);
            
            if ((m == NULL) || (buffer[0] != 'P'))
                return 0;
            param = odk_submatch_copy(buffer, m, 1);
            k = atoi(param);
            free(param);
            
            if ((k < 1) || (k > 32000))
                return 0;
                
            param = (char*)malloc(sizeof(char) * k+1);
            
            memset(param, 0, k+1);
            s = 0;
            
            while (s < k){
                r = auxsock->Read(buffer, 255);
                memcpy((char*)((int)param+s), buffer, r);
                s += r;
            }
            msg->PushParam(param);
        }
        
        msgQueue.push(msg);
        return 1;
    }
*/

int SocketIPC::PeekMessage(){
        
        if(FetchMessage2())
            return 1;
            
        if (msgQueue.size() > 0)
            return 1;
            
        return 0;
}
    
int SocketIPC::PushMessage(IPCMessage *msg){
        char *tmp, buffer[50];
        int l, blen;
        
        tmp = msg->GetMessageName();
        l = strlen(tmp);
        
        sprintf(buffer, "MSG[%i][%i]", l, msg->ParamCount());
        blen = strlen(buffer);
        
        sock->Write(buffer, blen);
        sock->Write(tmp, l);
        
        while(tmp = msg->PopParam()){
            l = strlen(tmp);
        
            sprintf(buffer, "PARAM[%i]", l);
            blen = strlen(buffer);
        
            sock->Write(buffer, blen);
            sock->Write(tmp, l);
            
            free(tmp);
        }
        
        return 1;
}
    
IPCMessage *SocketIPC::PopMessage(){
        IPCMessage *msg;
        
        msg = msgQueue.front();
        msgQueue.pop();
        
        return msg;
}

int SocketIPC::RawRead(char *buff, int size){
	return sock->Read(buff, size);
}

int SocketIPC::RawWrite(char *buff, int size){
	return sock->Write(buff, size);
}

